package com.dj.common.web.exception;

import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.dj.common.web.rest.ResponsePack;
import com.dj.common.web.rest.RestApiError;

/**
 * Controller抛出的异常会在这里处理,然后返回一个带有错误代码，错误消息的json串返回给客户端
 *
 */
@ControllerAdvice
public class CoreResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

	private static final Logger logger = LoggerFactory.getLogger(CoreResponseEntityExceptionHandler.class);

	private static final String EXCEPTION_MESSAGE_KEY = "exceptionMessage";

	final static private String DEFAULT_MESSAGE = "异常错误，请联系系统管理员.";

	private static final String DEFAULT_HTTP_LOCALE_PREFIX = "default.HTTP_";

	private static final Object[] EMPTY_PARAMS = new Object[] {};

	@Autowired
	private ReloadableResourceBundleMessageSource messageBundle;

	/**
	 * 当Controller抛出InvalidRequestException时，该方法会捕捉该异常，可以扩展捕捉其他异常
	 * @param ex
	 * @param request
	 * @return
	 */
	@ExceptionHandler(value = { InvalidRequestException.class })
	public ResponseEntity<ResponsePack<Object>> handleServiceException(final DjException ex, final WebRequest request) {

		HttpStatus responseStatus = getResponseStatus(ex);
		RestApiError rae = packErrorResponse(ex, responseStatus);
		ResponsePack<Object> env = new ResponsePack<>(rae);
		return new ResponseEntity<>(env, responseStatus);
	}

	private HttpStatus getResponseStatus(final DjException ex) {
		HttpStatus responseStatus = HttpStatus.INTERNAL_SERVER_ERROR;

		if (ex instanceof InvalidRequestException) {
			responseStatus = HttpStatus.BAD_REQUEST;
		}

		return responseStatus;
	}

	/**
	 * 捕捉Exception异常
	 * @param ex
	 * @param body
	 * @param request
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	protected ResponseEntity<Object> handleExceptionInternal(final Exception ex, final Object body,
			final WebRequest request) {
		return handleExceptionInternal(ex, body, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
	}
	
//	@ExceptionHandler(Exception.class)
//	public ModelAndView resolveException(Exception ex, final Object body) {
//		ModelAndView mv = new ModelAndView("error");
//		mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>"));
//		return mv;
//	}

	@Override
	protected ResponseEntity<Object> handleExceptionInternal(final Exception ex, final Object body,
			final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
		logger.error("Internal Exception thrown", ex);

		if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
			request.setAttribute("javax.servlet.error.exception", ex, RequestAttributes.SCOPE_REQUEST);
		}

		RestApiError rae = packErrorResponse(ex, status);
		ResponsePack<Object> env = new ResponsePack<>(rae);

		if (headers != null) {
			return new ResponseEntity<Object>(env, headers, status);
		}

		return new ResponseEntity<Object>(env, status);
	}

	private RestApiError packErrorResponse(final DjException ex, final HttpStatus responseStatus) {
		RestApiError rae = new RestApiError();
		rae.setCode(ex.getErrorCode());
		rae.setMessage(getLocalizedMessage(ex.getErrorCode(), ex.getLocalizedErrorMessageParams()));
		return rae;
	}

	private RestApiError packErrorResponse(final Exception ex, final HttpStatus responseStatus) {
		RestApiError rae = new RestApiError();
		rae.setMessage(getLocalizedMessage(responseStatus));
		return rae;
	}

	/**
	 * 
	 * @param status
	 * @return 本地化语言
	 */
	private String getLocalizedMessage(final HttpStatus status) {
		Locale locale = LocaleContextHolder.getLocale();
		String message = messageBundle.getMessage(DEFAULT_HTTP_LOCALE_PREFIX + status, EMPTY_PARAMS, DEFAULT_MESSAGE,
				locale);
		return message;
	}

	/**
	 * 
	 * @param errorCode
	 * @param errorParams
	 * @return 本地化语言
	 */
	public String getLocalizedMessage(final String errorCode, final Object[][] errorParams) {
		Locale locale = LocaleContextHolder.getLocale();
		String message = messageBundle.getMessage(errorCode, EMPTY_PARAMS, DEFAULT_MESSAGE, locale);
		message = replaceLocalizedErrorMessageParams(message, errorParams);

		return message;
	}

	/**
	 * 
	 * @param message
	 * @param emParams
	 * @return 替换后的本地化语言，本地化语言中的{}中的内容会被动态的替换
	 */
	private String replaceLocalizedErrorMessageParams(String message, final Object[][] emParams) {
		if (emParams != null && emParams.length > 0) {
			for (int i = 0; i < emParams.length; i++) {
				Object[] pair = emParams[i];
				if (pair != null && pair.length == 2 && pair[0] != null) {
					String key = "{" + pair[0].toString() + "}";
					String val = pair[1] != null ? pair[1].toString() : key;
					message = message.replace(key, val);
				}
			}
		}
		return message;
	}

	/**
	 * 
	 * @param emParams
	 * @return 异常消息参数映射
	 */
	private Map<String, Object> getErrorMessageParamMap(final Object[][] emParams) {
		Map<String, Object> emParamMap = new LinkedHashMap<>();
		if (emParams != null && emParams.length > 0) {
			for (int i = 0; i < emParams.length; i++) {
				Object[] pair = emParams[i];
				if (pair != null && pair.length == 2 && pair[0] != null) {
					String key = pair[0].toString();
					Object val = pair[1];
					emParamMap.put(key, val);
				}
			}
		}

		return emParamMap;
	}

}
